package nl.saxion.dbi;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

import nl.saxion.dbi.api.Rdbms;
import nl.saxion.dbi.api.RdbmsImpl;
import nl.saxion.dbi.dao.DatabaseBuilder;
import nl.saxion.dbi.dao.DatabaseManager;
import nl.saxion.dbi.exception.DatabaseAlreadyExistsException;
import nl.saxion.dbi.exception.DatabaseDoesNotExistsException;
import nl.saxion.dbi.exception.FieldAllreadyExistsException;
import nl.saxion.dbi.exception.FieldNotFoundException;
import nl.saxion.dbi.exception.IntegrityConstraintViolationException;
import nl.saxion.dbi.exception.InvalidTypeException;
import nl.saxion.dbi.exception.KeyAllreadyExistsException;
import nl.saxion.dbi.exception.KeyNotFoundException;
import nl.saxion.dbi.exception.KeyPairNotAllowedException;
import nl.saxion.dbi.exception.TableAlreadyExistsException;
import nl.saxion.dbi.exception.TableNotFoundException;
import nl.saxion.dbi.model.Database;
import nl.saxion.dbi.model.Key;
import nl.saxion.dbi.model.KeyPair;
import nl.saxion.dbi.model.KeyVeld;
import nl.saxion.dbi.model.Row;
import nl.saxion.dbi.model.Tabel;
import nl.saxion.dbi.model.Veld;

public class Bazenbase
{

	private Rdbms rdbms = new RdbmsImpl();

	public static void main(String[] args) throws IOException
	{

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		String input;

		Bazenbase bazenBase = new Bazenbase();
		boolean exit = false;

		while ((input = br.readLine()) != null)
		{
			String[] split = input.split(" ");

			if (split.length > 0)
			{
				switch (split[0])
				{
				case "clean":
					bazenBase.clean();
					System.out.println("Cleaned database");
					break;
				case "nieuw":
					bazenBase.nieuw(split);
					break;
				case "delete":
					bazenBase.delete(split);
					break;
				case "show":
					bazenBase.show(split);
					break;
				case "insert":
					bazenBase.insert(split);
					break;
				case "select":
					bazenBase.select(split);
					break;
				case "exit":
					exit = true;
					break;
				default:
					System.out.println("Commando " + split[0] + " onbekend");
					break;
				}
			}
			if (exit)
			{
				break;
			}
		}

		DatabaseManager.stopServer();
	}

	private void clean()
	{
		try
		{
			DatabaseBuilder builder = new DatabaseBuilder();
			builder.cleanDb();
			builder.buildDb();
			rdbms.clean();
		}
		catch (SQLException e)
		{
			e.printStackTrace();
		}
	}

	private void nieuw(String[] split)
	{
		if (split.length > 1)
		{
			switch (split[1])
			{
			case "database":
				createDatabase(split);
				break;
			case "tabel":
				createTable(split);
				break;
			case "veld":
				createVeld(split);
				break;
			case "key":
				createKey(split);
				break;
			case "primaryKey":
				addPrimaryKey(split);
				break;
			case "foreignKey":
				addForeignKey(split);
				break;
			default:
				System.out.println("commando nieuw kan niet uitgevoerd worden voor " + split[1]);
				break;

			}
		}
	}

	private void delete(String[] split)
	{
		if (split.length > 2)
		{
			switch (split[1])
			{
			case "database":
				deleteDatabase(split);
				break;
			case "key":
				deleteKey(split);
				break;
			case "veld":
				deleteVeld(split);
				break;
			case "tabel":
				deleteTabel(split);
				break;
			case "data":
				deleteData(split);
				break;
			default:
				System.out.println("commando delete kan niet uitgevoerd worden voor " + split[1]);
				break;
			}
		}
	}

	private void show(String[] split)
	{
		if (split.length > 1)
		{
			switch (split[1])
			{
			case "databases":
				showDatabases();
				break;
			case "tabellen":
				showTables(split);
				break;
			default:
				System.out.println("commando show kan niet uitgevoerd worden voor " + split[1]);
				break;
			}
		}
	}

	private void insert(String[] split)
	{
		if (split.length > 3)
		{
			String[] data = Arrays.copyOfRange(split, 3, split.length);
			try
			{
				rdbms.insertInto(split[1], split[2], data);
			}
			catch (IntegrityConstraintViolationException e)
			{
				System.out.println("Kon data niet toevoegen: " + e.getMessage());
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Kon data niet toevoegen: " + e.getMessage());
			}
		}
	}

	private void select(String[] split)
	{
		if (split.length > 1)
		{
			switch (split[1])
			{
			case "where":
				selectWhere(split);
				break;
			default:
				selectAll(split);
				break;
			}
		}
	}

	private void selectWhere(String[] split)
	{
		if (split.length > 4)
		{
			try
			{
				Tabel tabel = rdbms.selectFrom(split[2], split[3]);
				printTabelHeader(tabel);
				List<Row> rows = rdbms.selectWhere(split[2], split[3], split[4], split[5]);
				printTabelData(rows);
				System.out.println("Totaal aantal rijen: " + rows.size());
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Kon gegevens niet ophalen: " + e.getMessage());
			}
		}
	}

	private void selectAll(String[] split)
	{
		if (split.length > 2)
		{
			try
			{
				Tabel tabel = rdbms.selectFrom(split[1], split[2]);

				printTabelHeader(tabel);
				printTabelData(tabel.getData());

				System.out.println("Totaal aantal rijen: " + tabel.getData().size());
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Kon gegevens niet ophalen: " + e.getMessage());

			}
		}
	}

	private void printTabelHeader(Tabel tabel)
	{
		System.out.println("Data in tabel " + tabel.getNaam() + ": ");

		String veldString = "";
		String seperator = "";
		for (Veld veld : tabel.getVelden())
		{
			veldString += seperator + veld.getNaam();
			seperator = "\t";
		}
		System.out.println(veldString);
		for (int i = 0; i < veldString.length(); i++)
		{
			if (veldString.charAt(i) == '\t')
			{
				System.out.print("------");
			}
			else
			{
				System.out.print("-");
			}
		}
		System.out.print("\n");
	}

	private void printTabelData(List<Row> rows)
	{
		String seperator = "";
		for (Row row : rows)
		{
			seperator = "";
			String dataString = "";
			for (String string : row.getData())
			{
				dataString += seperator + string;
				seperator = "\t";
			}
			System.out.println(dataString);
		}

	}

	private void createDatabase(String[] split)
	{
		if (split.length > 2)
		{
			try
			{
				rdbms.createDatabase(split[2]);
			}
			catch (DatabaseAlreadyExistsException e)
			{
				System.out.println("Kon database \"" + split[2] + "\" niet aanmaken, database bestaat al");
			}
		}
		else
		{
			System.out.println("Geef een database naam op om aan te maken");
		}
	}

	private void deleteDatabase(String[] split)
	{
		if (split.length > 2)
		{
			try
			{
				rdbms.deleteDatabase(split[2]);
			}
			catch (IntegrityConstraintViolationException e)
			{
				System.out.println("Kon database niet verwijderen: " + e.getMessage());
			}
		}
		else
		{
			System.out.println("Geef een database naam op om te verwijderen");
		}
	}

	private void showDatabases()
	{
		List<Database> databases = rdbms.showDatabases();
		System.out.println("Alle databases:");
		for (Database database : databases)
		{
			System.out.println("\t" + database.getNaam() + ", " + database.getId());
		}
	}

	private void createTable(String[] split)
	{
		if (split.length > 3)
		{
			try
			{
				rdbms.createTabel(split[2], split[3]);
			}
			catch (TableAlreadyExistsException e)
			{
				System.out.println("Kon tabel " + split[2] + " in database " + split[3]
					+ " niet aanmaken, tabel bestaat al");
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Kon tabel " + split[2] + " in database " + split[3] + " niet aanmaken"
					+ e.getMessage());
			}
		}
	}

	private void showTables(String[] split)
	{
		if (split.length > 2)
		{
			try
			{
				Database database = rdbms.showDatabase(split[2]);
				System.out.println("Tabellen in database: " + split[2]);
				for (Tabel tabel : database.getTabellen())
				{
					System.out.println("\t" + tabel.getNaam());
					for (Veld veld : tabel.getVelden())
					{
						System.out.println("\t\t" + veld.getNaam() + ", " + veld.getType());
					}

					if (!tabel.getKeys().isEmpty())
					{
						System.out.println("\t\tKeys:");
						for (Key key : tabel.getKeys())
						{
							String foreignKey;
							if (tabel.getPrimaryKey() != null && tabel.getPrimaryKey().getId() == key.getId())
							{
								foreignKey = "\t\t\t* " + key.getNaam() + "(";
							}
							else
							{
								foreignKey = "\t\t\t" + key.getNaam() + "(";
							}
							String fkSeperator = "";
							for (KeyVeld keyVeld : key.getVelden())
							{
								foreignKey += fkSeperator + keyVeld.getVeld().getNaam();
								fkSeperator = ", ";
							}
							foreignKey += ")";

							Key toKey = getToKey(database, key);
							if (toKey != null)
							{
								foreignKey += " -> " + toKey.getNaam();
							}
							System.out.println(foreignKey);
						}
					}
				}
			}
			catch (IllegalStateException e)
			{
				System.out.println("Kon tabellen niet weergeven: " + e.getMessage());
			}
		}
	}

	private Key getToKey(Database database, Key fromKey)
	{
		for (KeyPair keyPair : database.getKeyPairs())
		{
			if (keyPair.getFromKey().getId() == fromKey.getId())
			{
				return keyPair.getToKey();
			}
		}
		return null;
	}

	private void createVeld(String[] split)
	{
		if (split.length > 6)
		{
			try
			{
				Boolean notNull = parseBoolean(split[6]);
				if (notNull != null)
				{
					rdbms.addVeld(split[2], split[3], split[4], split[5], notNull);
				}
				else
				{
					System.out.println(split[6] + " is geen geldige boolean waarde");
				}
			}
			catch (FieldAllreadyExistsException e)
			{
				System.out.println("Kon veld niet toevoegen, veld " + e.getMessage() + " bestaat al");
			}
			catch (InvalidTypeException e)
			{
				System.out.println(e.getMessage() + " is geen geldig type");
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Kon veld niet toevoegen : " + e.getMessage());
			}
		}
	}

	private void createKey(String[] split)
	{
		if (split.length > 6)
		{
			try
			{
				Boolean isUnique = parseBoolean(split[5]);
				String[] veldnamen = Arrays.copyOfRange(split, 6, split.length);
				if (isUnique != null)
				{
					rdbms.createKey(split[2], split[3], split[4], isUnique, veldnamen);
				}
			}
			catch (FieldNotFoundException e)
			{
				System.out.println("Veld " + e.getMessage() + " kon niet gevonden worden");
			}
			catch (KeyAllreadyExistsException e)
			{
				System.out.println("Key " + e.getMessage() + " bestaat al");
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[2] + " bestaat niet");
			}
		}
	}

	private void addPrimaryKey(String[] split)
	{
		if (split.length > 4)
		{
			try
			{
				rdbms.addPrimaryKey(split[2], split[3], split[4]);
			}
			catch (KeyNotFoundException e)
			{
				System.out.println("Key " + e.getMessage() + " bestaat niet");
			}
			catch (TableNotFoundException e)
			{
				System.out.println("Tabel " + e.getMessage() + " bestaat niet");
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[2] + " bestaat niet");
			}
		}
	}

	private void addForeignKey(String[] split)
	{
		if (split.length > 4)
		{
			try
			{
				rdbms.addForeignKey(split[2], split[3], split[4]);
			}
			catch (KeyNotFoundException e)
			{
				System.out.println("Key " + e.getMessage() + " bestaat niet");
			}
			catch (KeyPairNotAllowedException e)
			{
				System.out.println("KeyPair " + e.getMessage()
					+ " bestaat al of er is al een verwijzing vanaf deze key");
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[2] + " bestaat niet");
			}
		}
	}

	private void deleteKey(String[] split)
	{
		if (split.length > 4)
		{
			try
			{
				rdbms.deleteKey(split[2], split[3], split[4]);
			}
			catch (IntegrityConstraintViolationException e)
			{
				System.out.println("Kan key " + e.getMessage() + " niet verwijderen");
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[2] + " bestaat niet");
			}
		}
	}

	private void deleteVeld(String[] split)
	{
		if (split.length > 4)
		{
			try
			{
				rdbms.deleteVeld(split[2], split[3], split[4]);
			}
			catch (IntegrityConstraintViolationException e)
			{
				System.out.println("Kon veld niet verwijderen, er is nog een constraint op : " + e.getMessage());
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[2] + " bestaat niet");
			}
		}
	}

	private void deleteTabel(String[] split)
	{
		if (split.length > 3)
		{
			try
			{
				rdbms.deleteTabel(split[2], split[3]);
			}
			catch (IntegrityConstraintViolationException e)
			{
				System.out.println("Tabel kon niet verwijderd worden, er is nog een constraint op :" + e.getMessage());
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[2] + " bestaat niet");
			}
		}
	}

	private void deleteData(String[] split)
	{
		if (split.length > 1)
		{
			switch (split[2])
			{
			case "where":
				deleteDataWhere(split);
				break;
			default:
				deleteAllData(split);
				break;
			}
		}
	}

	private void deleteAllData(String[] split)
	{
		if (split.length > 3)
		{
			try
			{
				rdbms.deleteData(split[2], split[3]);
			}
			catch (IntegrityConstraintViolationException e)
			{
				System.out.println("Kon gegevens niet verwijderen: " + e.getMessage());
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[2] + " bestaat niet");
			}
		}
	}

	private void deleteDataWhere(String[] split)
	{
		if (split.length > 6)
		{
			try
			{
				rdbms.deleteDataWhere(split[3], split[4], split[5], split[6]);
			}
			catch (IntegrityConstraintViolationException e)
			{
				System.out.println("Kon gegevens niet verwijderen: " + e.getMessage());
			}
			catch (DatabaseDoesNotExistsException e)
			{
				System.out.println("Database " + split[3] + " bestaat niet");
			}
		}

	}

	private Boolean parseBoolean(String bool)
	{
		Boolean result = null;
		if (bool.equals("true"))
		{
			result = true;
		}
		else if (bool.equals("false"))
		{
			result = false;
		}
		return result;
	}
}
